package org.atf.mgr;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.management.AttributeList;
import javax.management.InstanceNotFoundException;
import javax.management.MalformedObjectNameException;
import javax.management.NotificationFilterSupport;
import javax.management.ObjectName;

import org.atf.model.dao.PreferenceDAO;
import org.atf.model.entity.Preference;
import org.atf.model.entity.Profile;
import org.atf.model.entity.Server;
import org.atf.services.TaskStatus;
import org.atf.utils.ATFUtils;


import com.ibm.websphere.management.AdminClient;
import com.ibm.websphere.management.AdminClientFactory;
import com.ibm.websphere.management.NotificationConstants;
import com.ibm.websphere.management.Session;
import com.ibm.websphere.management.configservice.ConfigService;
import com.ibm.websphere.management.configservice.ConfigServiceHelper;
import com.ibm.websphere.management.configservice.ConfigServiceProxy;
import com.ibm.websphere.management.exception.ConnectorException;
import com.ibm.wsspi.ssl.RetrieveSignersHelper;

public class WASProfileManagerImpl extends  ProfileManager {
	private  Profile profile;
	private String soapCfgURL;
	private String sslCfgURL;
	private String trustStore;
	private String trustStorePassword="WebAS";
	private String keyStore;
	private String keyStorePassword="WebAS";
	private String rootFolder;
	private boolean lostConnection;
	private HashMap mbeanMap;
	private int maxRetryCount;
	private boolean autoRegisterNotification= true;
	static boolean traceEnabled = false;
	private Properties props;
	private Integer waitTime = 1800;
	private String wasInstallPath;
	private AdminClient adminClient;
	private ConfigService configService;
	private ATFNotifications notifications;
	private Calendar connectedTime;
	private String cacheDisabled="true";
	
	public WASProfileManagerImpl() {
		soapCfgURL = "soap.client.props";
        sslCfgURL = "ssl.client.props";
        trustStore="ClientTrustFile.jks";
        keyStore="ClientKeyFile.jks";
        lostConnection = true;
        maxRetryCount = 5;
        mbeanMap = new HashMap();
        rootFolder=System.getProperty("user.home");
	}

	@Override
	public String startServer(Server server) {
		// TODO Auto-generated method stub
		String status=serverStatus(server);
	    Boolean processStatus=false;
	    if(status.equalsIgnoreCase("STOPPED")){
		    try {
				ObjectName nodeAgentObj=getNodeMbeanObj(server.getNodename());
				String opName = "launchProcess";
				String signature[] = { "java.lang.String", "java.lang.Integer" }; 
				Object params[] = { server.getServername(), waitTime }; 
	            if(nodeAgentObj != null){
	                processStatus=(Boolean)adminClient.invoke(nodeAgentObj, opName, params, signature);
	                getNotification().addNotification(new TaskStatus(
	        				String.valueOf(server.getId()),
	        				server.getId(),
	        				getProfileDetails().getId(),
	        				TaskStatus.NOTIFICATION,
	        				server.getServername(),
	        				"Status:STARTED "+processStatus));
	                
	            }
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				processStatus=false;
			} 
	     }
	     
	    if(processStatus){
	    	return "STARTED";
	    }else{
	    	return "UNKNOWN";
	    }
	}

	@Override
	public String stopServer(Server server) {
		// TODO Auto-generated method stub
		String status=serverStatus(server);
	    String processStatus="";
	     if(status.equalsIgnoreCase("RUNNING")){
		         try {
		             ObjectName serverMbean=getServerMBean(server.getNodename(), server.getServername());
		             String opName = "stop";
		             String signature[] = null;
		             String params[] = null;
		             adminClient.invoke(serverMbean, opName, params, signature);
		             long start = System.currentTimeMillis();
		     		 while(!serverStatus(server).equalsIgnoreCase("stopped")){
		     			long elapsedTimeMillis = System.currentTimeMillis() - start;
		     			float elapsedTimeSec = elapsedTimeMillis/1000F;
		     			if( elapsedTimeSec >= waitTime){
		     				break;
		     			}
		     			try {
		     				Thread.sleep(2000);
		     			} catch (InterruptedException e) {
		     				// TODO Auto-generated catch block
		     				e.printStackTrace();
		     			}
		     		 }
		             processStatus="stopped";
		             getNotification().addNotification(new TaskStatus(
		     				String.valueOf(server.getId()),
		     				server.getId(),
		     				getProfileDetails().getId(),
		     				TaskStatus.NOTIFICATION,
		     				server.getServername(),
		     				"Status: Stopped "+processStatus));
		         }catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				processStatus="UNKNOWN";
		         } 
	     }
	     return processStatus;
	
		
	}

	@Override
	public String restartServer(Server server) {
		// TODO Auto-generated method stub
		stopServer(server);
		long start = System.currentTimeMillis();
		while(!serverStatus(server).equalsIgnoreCase("stopped")){
			long elapsedTimeMillis = System.currentTimeMillis() - start;
			float elapsedTimeSec = elapsedTimeMillis/1000F;
			if( elapsedTimeSec >= waitTime){
				break;
			}
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return startServer(server);
	}

	@Override
	public String serverStatus(Server server) {
		// TODO Auto-generated method stub
		String processStatus="UNKNOWN";
		if(adminClient == null)
            return "UNKNOWN";
		try {
			ObjectName nodeAgentObj=getNodeMbeanObj(server.getNodename());
			String opName = "getProcessStatus";
			String signature[] = { "java.lang.String" };
			String params[] = { server.getServername() };
             if(nodeAgentObj != null){
                 processStatus=(String)adminClient.invoke(nodeAgentObj, opName, params, signature);
                 getNotification().addNotification(new TaskStatus(
         				String.valueOf(server.getId()),
         				server.getId(),
         				getProfileDetails().getId(),
         				TaskStatus.NOTIFICATION,
         				server.getServername(),
         				"Status: "+processStatus));
             }else{
            	 System.err.println("Nodeagent is not running:  "+server.getNodename());
             } 	
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			processStatus="UNKNOWN";
		} 
		
		return processStatus;
	}

	@Override
	public boolean testConnection() {
		// TODO Auto-generated method stub
		if(adminClient == null){
			retryConnection();
		}
		if(adminClient != null){
			 try {
				if(adminClient.getDefaultDomain() != null){
					lostConnection= false;
					return true;
				 }
			} catch (ConnectorException e) {
				// TODO Auto-generated catch block
				lostConnection= true;
				e.printStackTrace();
			}
		 }
		 
		return false;
		
	}

	@Override
	public Profile getProfileDetails() {
		// TODO Auto-generated method stub
		return this.profile;
	}

	@Override
	public void initialize() {
		// TODO Auto-generated method stub
		System.out.println("initializing the profile ....");
		PreferenceDAO preferenceDAO= new PreferenceDAO();
		Preference sslConfigPref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "SSL_CONFIG_PROP", "PROFILE");
		if(sslConfigPref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(sslConfigPref.getpValue())){
				sslCfgURL=sslConfigPref.getpValue();
			}
		}
		Preference soapConfigPref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "SOAP_CONFIG_PROP", "PROFILE");
		if(soapConfigPref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(soapConfigPref.getpValue())){
				soapCfgURL=soapConfigPref.getpValue();
			}
		}
		
		Preference trustFilePref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "TRUST_STORE", "PROFILE");
		if(trustFilePref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(trustFilePref.getpValue())){
				trustStore=trustFilePref.getpValue();
			}
		}
		
		Preference keyStorePref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "KEY_STORE", "PROFILE");
		if(keyStorePref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(keyStorePref.getpValue())){
				keyStore=keyStorePref.getpValue();
			}
		}
		Preference trustFilePasswordPref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "TRUST_STORE_PASSWORD", "PROFILE");
		if(trustFilePasswordPref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(trustFilePasswordPref.getpValue())){
				trustStorePassword=trustFilePasswordPref.getpValue();
			}
		}
		Preference keyStorePasswordPref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "KEY_STORE_PASSWORD", "PROFILE");
		if(keyStorePasswordPref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(keyStorePasswordPref.getpValue())){
				keyStorePassword=keyStorePasswordPref.getpValue();
			}
		}
		Preference wasNotificationPref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "DISABLE_WAS_NOTIFICATION", "PROFILE");
		if(wasNotificationPref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(wasNotificationPref.getpValue())){
				if(wasNotificationPref.getpValue().equalsIgnoreCase("true")){
					autoRegisterNotification=false;
				}
			}
		}
		Preference wasInstallPathPref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "WAS_INSTALL_PATH", "PROFILE");
		if(wasInstallPathPref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(wasInstallPathPref.getpValue())){
				wasInstallPath=wasInstallPathPref.getpValue();
			}
		}
		
		Preference wasWaitTimePref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "WAS_CONNECTION_WAIT_TIME", "PROFILE");
		if(wasWaitTimePref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(wasWaitTimePref.getpValue())){
				waitTime=Integer.parseInt(wasWaitTimePref.getpValue());
			}
		}
		
		Preference wasCacheDisabled= preferenceDAO.getPreferenceByName(profile.getId(), 0, "WAS_ADMINCLIENT_CACHE", "PROFILE");
		if(wasCacheDisabled != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(wasCacheDisabled.getpValue())){
				cacheDisabled=wasCacheDisabled.getpValue();
			}
		}
		Preference wasConnRetryPref= preferenceDAO.getPreferenceByName(profile.getId(), 0, "WAS_CONNECTION_RETRY", "PROFILE");
		if(wasConnRetryPref != null){
			if(org.apache.commons.lang3.StringUtils.isNotEmpty(wasConnRetryPref.getpValue())){
				maxRetryCount=Integer.parseInt(wasConnRetryPref.getpValue());
			}
		}
		System.out.println("Connecting to remote profile ....");
		doConnect();
		
	}

	@Override
	public boolean isConnected() {
		// TODO Auto-generated method stub
		
		 if(getAdminClient() == null){
			 return false;
		 }
		 try {
	        	System.out.println(adminClient.getDefaultDomain()+ " Mbean Count: "+adminClient.getMBeanCount());
				return getAdminClient().getServerMBean() != null;
			} catch (ConnectorException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		 return false;
			 
	}

	@Override
	public boolean retryConnection() {
		// TODO Auto-generated method stub
		
		for(int i=0; i<maxRetryCount; i++){
			if(!isConnected()){
				doConnect();
			}else{
				return true;
			}
		}
		return false;
	}

	@Override
	public List<Server> getServersList() {
		// TODO Auto-generated method stub
		System.out.println("getServersList");
		List<Server> slist= new ArrayList<Server>();
		String cell=getCellName();
		System.out.println("cell: "+cell);
		Session session;
		try {
			session = new Session();
			ObjectName nodePattern = ConfigServiceHelper.createObjectName(null, "Node");
			ObjectName serverPattern = ConfigServiceHelper.createObjectName(null, "ServerIndex");
			ObjectName configData[] = getConfigService().queryConfigObjects(session, null, nodePattern, null);
			
			for(int i = 0; i < configData.length; i++){
				System.out.println(configData[i]);
				String nodeName = ConfigServiceHelper.getDisplayName(configData[i]);
				ObjectName nodeScope = getConfigService().resolve(session, (new StringBuilder("Node=")).append(nodeName).toString())[0];
				ObjectName serversData[] = getConfigService().queryConfigObjects(session, nodeScope, serverPattern, null);
				for(int j = 0; j < serversData.length; j++){
					System.out.println(serversData[j]);
					List serverEntries = (ArrayList)getConfigService().getAttribute(session, serversData[j], "serverEntries");
					String hostName = (String)getConfigService().getAttribute(session, serversData[j], "hostName");
					System.out.println("hostName:"+hostName);
					for(Iterator iterator = serverEntries.iterator(); iterator.hasNext();){
						
						AttributeList serverAttr = (AttributeList)iterator.next();
						String serverType = (String)ConfigServiceHelper.getAttributeValue(serverAttr, "serverType");
						String serverName = (String)ConfigServiceHelper.getAttributeValue(serverAttr, "serverName");
						if(serverType.equalsIgnoreCase("APPLICATION_SERVER")){
							Server server= new Server();
							server.setServername(serverName);
							server.setHostname(hostName);
							server.setNodename(nodeName);
							server.setCellName(cell);
							String status=serverStatus(server);
							server.setStatus(status);
							slist.add(server);
						}
						
					}
				}
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		
	
		return slist;
	}

	@Override
	public List<String> getNodes() {
		// TODO Auto-generated method stub
		List<String> nodes= new ArrayList<String>();
		
		Session session;
		if(getAdminClient() == null){
			 doConnect();
		}
		try {
			session = new Session();
			ObjectName nodePattern = ConfigServiceHelper.createObjectName(null, "Node");
			ObjectName configData[] = getConfigService().queryConfigObjects(session, null, nodePattern, null);
			for(int i = 0; i < configData.length; i++){
				String nodeName = ConfigServiceHelper.getDisplayName(configData[i]);
				nodes.add(nodeName);
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
		return nodes;
	}

	@Override
	public void setProfileDetails(Profile profile) {
		// TODO Auto-generated method stub
		this.profile=profile;
	}

	@Override
	public void setNotification(ATFNotifications notification) {
		// TODO Auto-generated method stub
		this.notification= notification;
	}

	@Override
	public ATFNotifications getNotification() {
		// TODO Auto-generated method stub
		return this.notification;
	}
	
	public boolean doConnect(){
		System.out.println("Inside doConnect....");
		Properties props = new Properties();
		props.put(AdminClient.CACHE_DISABLED, cacheDisabled);
		props.put(AdminClient.CONNECTOR_TYPE, AdminClient.CONNECTOR_TYPE_SOAP);
		props.put(AdminClient.CONNECTOR_SOAP_REQUEST_TIMEOUT, waitTime);
		if(profile.getSecurity().equalsIgnoreCase("true")){
			props.put(AdminClient.CONNECTOR_SECURITY_ENABLED, Boolean.toString(true));
			props.put(AdminClient.USERNAME, profile.getUsername());
			props.put(AdminClient.PASSWORD,profile.getPassword());	
			
			
		}
		
		if(org.apache.commons.lang3.StringUtils.isNotEmpty(wasInstallPath)){
			System.setProperty("was.install.root", wasInstallPath);
	        System.setProperty("user.root", wasInstallPath);
	        props.put("user.root",wasInstallPath);
	        if(System.getProperty("java.vendor").contains("Sun") || System.getProperty("java.vendor").contains("Oracle")){
	        	soapCfgURL =(new StringBuilder(String.valueOf("file:"))).append(wasInstallPath).append("/properties/").append("soap.client.props.sun").toString();
	        	sslCfgURL = (new StringBuilder(String.valueOf("file:"))).append(wasInstallPath).append("/properties/").append("ssl.client.props.sun").toString();
	        }
	        if(soapCfgURL.equalsIgnoreCase("soap.client.props")){
	        	 soapCfgURL=(new StringBuilder(String.valueOf("file:"))).append(wasInstallPath).append("/properties/").append(soapCfgURL).toString();
	        }
	        if(sslCfgURL.equalsIgnoreCase("ssl.client.props")){
	        	sslCfgURL = (new StringBuilder(String.valueOf("file:"))).append(wasInstallPath).append("/properties/").append(sslCfgURL).toString();
	        }
	        
	        
		}
		
		props.put(AdminClient.CONNECTOR_HOST, profile.getHostname());
		props.put(AdminClient.CONNECTOR_PORT, String.valueOf(profile.getPort()));
		props.put(AdminClient.CONNECTOR_SOAP_CONFIG, soapCfgURL);
		props.put("com.ibm.SSL.ConfigURL", sslCfgURL);
		
		System.setProperty("com.ibm.SOAP.ConfigURL", soapCfgURL);
	    System.setProperty("com.ibm.SSL.ConfigURL", sslCfgURL);
        System.setProperty("org.apache.soap.util.net.SSLUtils.buildSSLSocket.timeout",String.valueOf(waitTime));
        System.setProperty("com.ibm.SOAP.requestTimeout", String.valueOf(waitTime));
        System.setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(waitTime));
        System.setProperty("sun.rmi.transport.proxy.connectTimeout", String.valueOf(waitTime));
        System.setProperty("org.apache.soap.util.net.SSLUtils.buildSSLSocket.timeout", String.valueOf(waitTime));
        System.setProperty("com.ibm.ws.management.connector.soap.keepAlive", "true");
        System.setProperty("com.ibm.websphere.thinclient", "true");
        
        props.put("com.ibm.SOAP.requestTimeout", String.valueOf(waitTime));
        props.put("sun.net.client.defaultConnectTimeout", String.valueOf(waitTime));
        props.put("sun.rmi.transport.proxy.connectTimeout", String.valueOf(waitTime));
        props.put("org.apache.soap.util.net.SSLUtils.buildSSLSocket.timeout", String.valueOf(waitTime));
        props.put("com.ibm.ws.management.connector.soap.keepAlive", "true");
        props.put("com.ibm.websphere.thinclient", "true");
        
	    System.out.println(System.getProperties());
        
        System.out.println("Props..."+props);
        if(profile.getSecurity().equalsIgnoreCase("true")){
        	 RetrieveSignersHelper.getInstance().autoAcceptSignerAndStoreInTrustStore();
        	 System.out.println("Auto Accept Signer");
        }
        try {
        	System.out.println("creating the Admin Client");
			adminClient = AdminClientFactory.createAdminClient(props);		
        } catch (Exception e) {
			// TODO Auto-generated catch block
        	lostConnection= true;
			e.printStackTrace();
		}
        if(adminClient != null){
        	System.out.println("Admin Client is not Null.. will try to register notification");
        	if(autoRegisterNotification && notification != null){
        		System.out.println("Registering the notification for : "+profile.getProfileName());
        		registerNotificationListener();
        		
        	}else{
        		System.out.println("Notification Listener is not registered");
        	}
        }
        if(adminClient != null){
        	lostConnection= false;
        	try {
        		System.out.println(adminClient.getDefaultDomain()+ " Mbean Count: "+adminClient.getMBeanCount());
				System.out.println("Connected to the WAS successfully.");
				setConnectedTime(Calendar.getInstance());
	        	return true;
			} catch (ConnectorException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	
        }
        
        
		return false;
		
	}
	public String getCellName()
	  {
		 if(adminClient == null)
			 return null;
		 
	    try {
			return adminClient.getServerMBean().getKeyProperty( "cell" );
		} catch (NullPointerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ConnectorException e) {
			// TODO Auto-generated catch block SitescopeAlertManager
			e.printStackTrace();
		}
	    return null;
	  }
	public void registerNotificationListener()
    {
        // Register this object as a listener for notifications from all
        // ThreadMonitor MBean.  Note this is doing a wildcard search and
        // we add a filter so this is only listening for the Hung thread.
        try
        {
        	ObjectName query = new ObjectName("WebSphere:*");
        	NotificationFilterSupport filter = new NotificationFilterSupport();
            filter.enableType(NotificationConstants.TYPE_PROCESS_STOPPING);
            filter.enableType(NotificationConstants.TYPE_PROCESS_STOPPED);
            filter.enableType(NotificationConstants.TYPE_PROCESS_STARTING);
            filter.enableType(NotificationConstants.TYPE_PROCESS_RUNNING);
           
            getAdminClient().addNotificationListenerExtended(query, notification, filter, null);
            System.out.println("Registered for event notifications");
        }
        catch (Exception e)
        {
            System.out.println(e);
            e.printStackTrace();
        }
    }
	public void removeNotificationListener(AdminClient ac){
		 try
	        {
			 ObjectName query = new ObjectName("WebSphere:*");
			 System.out.println("Admin Connection Domain : "+ac.getDomainName());
			 if(autoRegisterNotification && notification != null){
				 getAdminClient().removeNotificationListenerExtended(query, notification);
				 System.out.println("Remove Notification listener");
			 }
			 
	        }
		 catch (Exception e)
	        {
	            System.out.println(e);
	            e.printStackTrace();
	            System.exit(-1);
	        }
	}
	private ObjectName getNodeMbeanObj(String nodeName) throws MalformedObjectNameException, NullPointerException, ConnectorException{
		ObjectName nodeObj;
		ObjectName queryName= new ObjectName("WebSphere:type=NodeAgent,node=" + nodeName + ",*");
		Set s = adminClient.queryNames(queryName, null); 
		if (!s.isEmpty()){
			nodeObj = (ObjectName)s.iterator().next();
			//adminClient.invoke()
			return nodeObj;
		}
		else{ 
			return null;
		}
	}
	private ObjectName getServerMBean(String nodeName, String serverName) throws ConnectorException {
	     ObjectName serverMBean=null;
	        try {
	            StringBuffer oNameQuery= new StringBuffer(41);
	                oNameQuery.append("WebSphere:*");
	                oNameQuery.append(",type=").append("Server");
	                oNameQuery.append(",node=").append(nodeName);
	                oNameQuery.append(",process=").append(serverName);
	            ObjectName partialName = new ObjectName(oNameQuery.toString());
	            Set s = adminClient.queryNames(partialName, null);
	            if (!s.isEmpty()) {
	                serverMBean = (ObjectName) s.iterator().next();
	            }
	        } catch (Exception exc) {
	            // will not occur
	            exc.printStackTrace();
	        }
	    return serverMBean;
	   }

	public AdminClient getAdminClient() {
		return adminClient;
	}

	public void setAdminClient(AdminClient adminClient) {
		this.adminClient = adminClient;
	}

	public ConfigService getConfigService() {
		System.out.println("getting ConfigService");
		if(configService == null){
			 try
	         {
	             configService = new ConfigServiceProxy(adminClient);
	             System.out.println("get the ConfigService: "+configService);
	         }
	         catch(InstanceNotFoundException e)
	         {
	        	
	             e.printStackTrace();
	         }
	         catch(ConnectorException e)
	         {
	        	
	             e.printStackTrace();
	         }
			 
		 }
		return configService;
	}

	public void setConfigService(ConfigService configService) {
		this.configService = configService;
	}

	@Override
	public java.util.Calendar getConnectedTime() {
		// TODO Auto-generated method stub
		return this.connectedTime;
	}

	@Override
	public void setConnectedTime(java.util.Calendar calendar) {
		// TODO Auto-generated method stub
		this.connectedTime=calendar;
		
	}

	@Override
	public void destroyProfile() {
		// TODO Auto-generated method stub
		if(isConnected()){
			removeNotificationListener(adminClient);
			adminClient=null;
		}
	}
	
	
}
